Add pathtext recipe for text along a path#5596
Merged
jkrumbiegel merged 21 commits intomasterfrom Apr 16, 2026
Merged
Conversation
First working prototype. Supports:
- Vector{Point2} (with NaN separators) and BezierPath inputs
- :left/:center/:right or fractional alignment along path
- Per-character colors, strokecolor, strokewidth
- Perpendicular offset via polyline offsetting (miter-based)
- :data and :pixel path space (text always pixel-sized)
- Reactive re-layout on camera changes via register_projected_positions!
Replace linearized polyline sampling with direct cubic Bézier evaluation when the input is a BezierPath. This gives smooth, continuous tangent rotations instead of the piecewise-constant tangents from the 30-point linearization. Key additions: - Cubic Bézier eval/deriv/second_deriv functions - 8-point Gauss-Legendre arc-length quadrature - Offset-curve arc length via curvature formula (speed·|1-d·κ|) - Binary-search inverse arc-length for parameter recovery - Control-point extraction/reassembly for projecting BezierPath to pixel - Separate convert_arguments methods (no more PointBased trait)
Layout RichText using layout_text() to get per-glyph positions, colors,
fonts, and sizes. Each glyph is wrapped as a single-char RichText
carrying its own style, letting the child text! handle font/color/size
natively per block.
Example: pathtext!(ax, path; text = rich("A", rich("B"; color=:red, font=:bold)))
The glyph y-origins from layout_text (e.g. subscript below baseline, superscript above) are now applied as perpendicular shifts from the path at each glyph position.
layout_text returns absolute y-origins (baseline at ~ascender height, not y=0). Subtract the baseline y so regular glyphs sit on the path and sub/superscripts shift perpendicular relative to it.
…tion align now accepts a (halign, valign) tuple like the text recipe: - halign: :left, :center, :right, or Real fraction (along path) - valign: :baseline, :bottom, :center, :top (perpendicular to path) valign uses font ascender/descender metrics to shift the text perpendicular to the path. offset remains as an additional manual pixel shift on top of valign.
Each glyph's rotation is now computed from the chord between its start and end positions on the path (spaced by the glyph's advance width), rather than the instantaneous tangent at the origin. This gives wider characters like "W" a rotation that better matches the arc they span.
- docs/src/reference/plots/pathtext.md with BezierPath, polyline, and RichText sub/superscript examples - attribute_examples for text (String vs RichText), align (valign comparison), and offset
Five reference tests covering: - align: halign (:left/:center/:right) and valign (:top/:center/:bottom) - String with per-char color vector and RichText with sub/superscripts - offset: perpendicular shift from path - polyline with NaN sub-path gap, BezierPath with LineTo/CurveTo/EllipticalArc - space = :data vs :pixel for the path coordinate system
When a glyph's start and end samples land on different sub-paths (separated by a NaN in a polyline or a MoveTo in a BezierPath), use the tangent at the start position instead of the chord, which would otherwise connect unrelated points across the gap.
- Shrink all pathtext refimages; hide axis decorations for denser framing. - Combine "string+richtext" and "offset" tests (both shown together on one curve with opposite offsets and per-char vs RichText styling). - Shorten polyline and BezierPath demo text to fit their smaller axes.
d7ad259 to
22b5767
Compare
Wraps another annotation style (passed positionally) and layers `pathtext` on top so a label follows the connection path. Adds a reference test and an attribute_examples entry. Includes a convert_attribute override for pathtext's `align` so PlotSpec doesn't coerce the symbolic `(halign, valign)` tuple into the numeric text-style align.
22b5767 to
48fa143
Compare
Collaborator
Benchmark ResultsSHA: 4351c550e58c02e88142bfdcdb5bd56ff8accff9 Warning These results are subject to substantial noise because GitHub's CI runs on shared machines that are not ideally suited for benchmarking. |
Split the text and path sides via inner-method dispatch: _layout_glyphs(text, ...) — AbstractString vs RichText _prepare_path_sampler(path, ...) — polyline vs BezierPath The outer _pathtext_layout is now one method that orchestrates both.
…t-length - _sample_bezierpath_at now binary-searches a cumulative arc-length table precomputed in _prepare_bezierpath, instead of scanning segments linearly per sample. - _cubic_inv_arclen uses 20 bisection iterations instead of 30 (≈1e-6 parameter precision, well below sub-pixel). - _layout_glyphs computes x_positions with a single-pass accumulator instead of cumsum(advances) .- advances. - total_text_len is now computed inside _place_glyphs_on_path instead of passed in (one fewer positional arg). - Trimmed a few WHAT-comments; removed an unreachable return.
ba9820f to
3ef33ec
Compare
5095f65 to
c035836
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a
pathtextrecipe for placingStringorRichTextalong aVector{<:Point2}orBezierPath. ForBezierPathinputs, glyph positions and tangents come from exact cubic-Bézier evaluation; arc-length quadrature is adapted fromkurbo(MIT).Also adds
Ann.Styles.WithText, which wraps another annotation style and layers apathtextlabel along the connection path.Example
Ann.Styles.WithTextChecks
:datavs:pixelspace, andAnn.Styles.WithTextattribute_examples